﻿//-----------------------------------------------------------------------------------------------------------
// WBFSSync Project by Omega Frost 
// http://wbfssync.codeplex.com/
//
// WBFSSync is Licensed under the terms of the 
// Microsoft Reciprocal License (Ms-RL)
//-----------------------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using WBFSe3.IO;
using WBFSe3.Wbfs;
using System.IO;
using WBFSe3.Interfaces;
using WBFSe3.Streams;
using WBFSe3.Native;

namespace WBFSe3.Formats
{
    //-------------------------------------------------------------------------------------------------------
    //
    //-------------------------------------------------------------------------------------------------------
    public class WbfsPWbfsCreator : WbfsFormatCreator
    {
        //---------------------------------------------------------------------------------------------------
        //
        //---------------------------------------------------------------------------------------------------
        WbfsDevice device;
        WbfsPath path;
        WbfsUsage usage;
        Stream stream;

        bool doubleLayer;
        int discidx;
        int sectorSize;
        ushort[] sectors;

        //---------------------------------------------------------------------------------------------------
        //
        //---------------------------------------------------------------------------------------------------
        public override WbfsPath Path { get { return this.path; } }
        public override int SectorSize { get { return this.sectorSize; } }


        //---------------------------------------------------------------------------------------------------
        //
        //---------------------------------------------------------------------------------------------------
        public override WbfsError Initialize(WbfsDriveInfo drive, String path, String filename,
            IWbfsGameInfo info, WbfsUsage usageTable, int sectorSize, int splitCount)
        {
            this.device = (WbfsDevice)drive;
            this.doubleLayer = info.DoubleLayer;
            this.sectorSize = this.device.WbfsSectorSize;
            this.usage = usageTable;
            this.path = new WbfsPath(path, new String[] { 
                String.Format("{0} {1}", info.Code, info.Name) });

            return WbfsError.Ok;
        }


        //---------------------------------------------------------------------------------------------------
        //
        //---------------------------------------------------------------------------------------------------
        public override long EstimateFileSize()
        {
            return (long)this.sectorSize *
                this.usage.CountOnes();
        }


        //---------------------------------------------------------------------------------------------------
        //
        //---------------------------------------------------------------------------------------------------
        public override WbfsError BeginCreate(out WbfsStream stream)
        {
            WbfsError error;
            ushort[] table;

            error = this.device.RequestDiscSlot(out this.discidx, this.path.Files[0]);
            if (error != WbfsError.Ok) { stream = null; return error; }

            error = this.device.RequestSectors(out table, this.usage.CountOnes());
            if (error != WbfsError.Ok) { stream = null; return error; }

            // Build the sector table
            
            int size = this.device.WbfsSecsPerDisc;
            this.sectors = new ushort[size];
            Array.Clear(this.sectors, 0, size);

            int j = 0;
            for (int i = 0; i < size; i++)
            {
                if (this.usage[i])
                    this.sectors[i] = table[j++];
            }

            // Create the stream

            String sDrive = WbfsNative.CreateDirectOpenString(this.path[0][0]);
            WbfsFileStream d = new WbfsFileStream(sDrive, this.sectorSize, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
            stream = new WbfsSectorStream(d, this.sectorSize, this.sectors);
            this.stream = stream;

            return WbfsError.Ok;
        }


        //---------------------------------------------------------------------------------------------------
        //
        //---------------------------------------------------------------------------------------------------
        public override WbfsError EndCreate(bool succeeded)
        {
            stream.Close();

            if (!succeeded)
            {
                // Free the alocated sectors
                this.device.FreeSectors(this.sectors);

                // Free the disc slot
                this.device.FreeDiscSlot(this.discidx);

                return WbfsError.Ok;
            }
            else
            {
                // Create the disc descriptor
                Byte[] desc = new Byte[this.device.DiscDescriptorSize];
                Array.Clear(desc, 0, desc.Length);

                // Read the disc header
                WbfsError error = this.device.Read(desc, 0,
                    (long)this.device.WbfsSectorSize * this.sectors[0],
                    WbfsConstants.WbfsDiscHCopySz);

                if (error != WbfsError.Ok)
                    return error;

                // Build the sector table
                for (int i = 0; i < this.sectors.Length; i++)
                {
                    ushort v = WbfsHelper.htons(this.sectors[i]);

                    BitConverter.GetBytes(v).CopyTo(desc,
                        WbfsConstants.WbfsDiscHCopySz + 2 * i);
                }

                // Update the head
                return this.device.SetDiscDescriptor(desc, this.discidx);
            }
        }
    }
}
